﻿#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
#endregion Using

namespace LloydGame
{
    public class SSAOImageProcessor : ImageProcessor
    {
        #region Class Fields
        protected RenderTarget2D m_occlusionTarget;
        protected RenderTarget2D m_blurTargetH;
        protected RenderTarget2D m_finalTarget;

        protected Texture2D m_randNormalTex;

        protected float m_sampleRadius = 5.0f;
        protected float m_distanceScale = 500.0f;
        #endregion Class Fields

        #region Properties
        public float sampleRadius
        {
            get
            {
                return m_sampleRadius;
            }
            set
            {
                m_sampleRadius = value;
            }
        }

        public float distanceScale
        {
            get
            {
                return m_distanceScale;
            }
            set
            {
                m_distanceScale = value;
            }
        }
        #endregion Properties

        // constructor
        public SSAOImageProcessor(Game p_game, int p_imageWidth, int p_imageHeight)
            : base(p_game, p_imageWidth, p_imageHeight)
        {
            m_occlusionTarget = new RenderTarget2D(
                p_game.GraphicsDevice,
                p_imageWidth,
                p_imageHeight,
                1,
                SurfaceFormat.Color);

            m_blurTargetH = new RenderTarget2D(
                p_game.GraphicsDevice,
                p_imageWidth,
                p_imageHeight,
                1,
                SurfaceFormat.Color);

            m_finalTarget = new RenderTarget2D(
                p_game.GraphicsDevice,
                p_imageWidth,
                p_imageHeight,
                1,
                SurfaceFormat.Color);

            m_depthStencil = new DepthStencilBuffer(
                m_occlusionTarget.GraphicsDevice,
                m_occlusionTarget.Width,
                m_occlusionTarget.Height,
                m_occlusionTarget.GraphicsDevice.DepthStencilBuffer.Format,
                m_occlusionTarget.MultiSampleType,
                m_occlusionTarget.MultiSampleQuality);

            m_effect = Game.Content.Load<Effect>("Effects\\SSAO");

            m_randNormalTex = Game.Content.Load<Texture2D>("Textures\\SSAO_randomNormals");
        }

        // compute the ambient occlusion
        public Texture2D process(Texture2D p_colorTex, Texture2D p_depthTex, Texture2D p_normalTex)
        {
            m_effect.CurrentTechnique = m_effect.Techniques["SSAO"];

            setCornerFrustum();

            // create an ambient occlusion map
            m_effect.Parameters["depthTex"].SetValue(p_depthTex);
            m_effect.Parameters["normalTex"].SetValue(p_normalTex);
            m_effect.Parameters["randNormalTex"].SetValue(m_randNormalTex);

            m_effect.Parameters["sampleRadius"].SetValue(m_sampleRadius);
            m_effect.Parameters["distanceScale"].SetValue(m_distanceScale);

            m_effect.Parameters["proj"].SetValue(((Game1)Game).camera.proj);

            Game.GraphicsDevice.SetRenderTarget(0, m_occlusionTarget);

            processPass(0);

            // blur the occlusion map horizontaly
            Game.GraphicsDevice.SetRenderTarget(0, m_blurTargetH);

            //Game.GraphicsDevice.SetRenderTarget(0, null);
            //return m_occlusionTarget.GetTexture();

            m_effect.Parameters["blurTexOffset"].SetValue(new Vector2(0.5f / m_imageWidth, 0.5f / m_imageHeight));

            m_effect.Parameters["occlusionTex"].SetValue(m_occlusionTarget.GetTexture());
            m_effect.Parameters["blurDirection"].SetValue(new Vector2(1.0f / m_imageWidth, 0.0f));
            
            processPass(1);

            // blur the occlusion map verticaly and blend it with the color texture
            Game.GraphicsDevice.SetRenderTarget(0, m_finalTarget);

            m_effect.Parameters["colorTex"].SetValue(p_colorTex);
            m_effect.Parameters["occlusionTex"].SetValue(m_blurTargetH.GetTexture());
            m_effect.Parameters["blurDirection"].SetValue(new Vector2(0.0f, 1.0f / m_imageHeight));

            processPass(2);

            Game.GraphicsDevice.SetRenderTarget(0, null);

            return m_finalTarget.GetTexture();
        }
    }
}
